﻿using Moki.Exceptions;
using Moki;
using Moki.Tests.Model;
using NUnit.Framework;

namespace Moki.Specification
{
    [TestFixture]
    public class VerifyTimesSpec
    {
        [TearDown]
        public void TearDown()
        {
            Mock.CleanUp();
        }

        [Test]
        public void Verify_was_called_once()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.GetSomething()).Returns("abc");

            test.DoSomething();

            Mock.VerifyCalledWithAnyArgs(() => test.DoSomething(), Times.AtLeast(1));

            Assert.Throws<VerifyException>(() => Mock.VerifyNotCalled(() => test.DoSomething()));
        }

        [Test]
        public void Verify_was_called_few_times()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.GetSomething()).Returns("abc");

            test.GetSomething();
            test.GetSomething();
            test.GetSomething();

            Mock.VerifyCalledWithAnyArgs(() => test.GetSomething(), Times.Exactly(3));
            Mock.VerifyCalledWithAnyArgs(() => test.GetSomething(), Times.AtLeast(2));
            Mock.VerifyCalledWithAnyArgs(() => test.GetSomething(), Times.AtMost(4));
        }

        [Test]
        public void Verify_not_stubbed_was_called_exactly_times()
        {
            ITests test = Mock.Create<ITests>();

            test.DoSomething();
            test.DoSomething();
 
            Mock.VerifyCalledWithAnyArgs(() => test.DoSomething(), Times.Exactly(2));
        }

        [Test]
        public void Verify_was_called_few_times_with_any_arg()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.ConvertToLang(Arg.Any<int>())).Returns("any int");

            test.ConvertToLang(1);
            test.ConvertToLang(2);
            test.ConvertToLang(3);

            Mock.VerifyCalledWithAnyArgs(() => test.ConvertToLang(Arg.Any<int>()), Times.Exactly(3));
            Mock.VerifyCalled(() => test.ConvertToLang(1), Times.Exactly(1));
            Mock.VerifyCalled(() => test.ConvertToLang(2), Times.Exactly(1));
            Mock.VerifyCalled(() => test.ConvertToLang(3), Times.Exactly(1));
            Mock.VerifyCalled(() => test.ConvertToLang(Arg.Matches<int>(i => i < 3)), Times.Exactly(2));
        }

        [Test]
        public void Verify_was_called_exactly_times_in_for_loop()
        {
            Test test = Mock.Create<Test>();

            for (int i = 1; i <= 10; i++)
            {
                for (int j = 1; j <= i; j++)
                {
                    test.Double(i);
                }
            }

            for (int i = 1; i <= 10; i++)
            {
                Mock.VerifyCalled(() => test.Double(i), Times.Exactly(i));
            }
        }

        [Test]
        [ExpectedException(typeof(VerifyException))]
        public void Verify_times_throws_exception()
        {
            ITests test = Mock.Create<ITests>();

            test.DoSomething();
            test.DoSomething();

            Mock.VerifyCalledWithAnyArgs(() => test.DoSomething(), Times.Exactly(1));
        }

    }
}
